/* $Id: ApplicationFrame.java,v 1.12 2007/10/19 08:16:01 gigabass Exp $
 *
 * Copyright (c) Guido Ludwig. All rights reserved.
 * Use is subject to license terms.
 */
package de.guidoludwig.af;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import com.jgoodies.forms.layout.Sizes;


/**
 * Main class of the Application Framework.
 *
 * @author <a href="mailto:jtrade@gigabss.de">Guido Ludwig</a>
 * @version $Revision: 1.12 $
 */
public class ApplicationFrame {

	private static final AFProperties PROPERTIES = new AFProperties(ApplicationFrame.class);

	public static final ApplicationFrame INSTANCE = new ApplicationFrame();

	private final List<Application> applications;

	private static final String MODAL = "@MODAL@";

	private final Action exitAction;

	private JPanel mainPanel;

	private CardPanel mainCard;

	private CardPanel menuCard;

	private Application activeApplication;

	private JComponent currentModal;

	private final Map<Application, Action> startActions;

	private JFrame mainFrame;

	private final Action helpAction;

	private final JLabel status;

	private ApplicationFrame() {
		applications = new ArrayList<Application>();
		exitAction = new ApplicationExitAction();
		helpAction = new HelpAction();
		startActions = new HashMap<Application, Action>();
		status = new JLabel("");
	}

	public void start() {
		try {
			SwingUtil.chooseLookAndFeel();
		} catch (Exception e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, e.getMessage());
		}
		JFrame main = build();
		main.setVisible(true);
	}

	/**
	 * Registers a new application
	 *
	 * @param application
	 */
	public void register(Application application) {
		if (!applications.contains(application)) {
			applications.add(application);
		}
	}

	/**
	 * Sets the Application into a modal mode and shows the component. To reset
	 * the modal mode, call {@link #resetModal()}. The method
	 * <ul>
	 * <li>Hides all internal actions (the toolbar) of the currently active
	 * application
	 * <li>Shows the component
	 * <li>disables all start actions of other applications.
	 * </ul>
	 *
	 * @param component
	 */
	public void setModal(JComponent component) {
		menuCard.showCard(MODAL);
		mainCard.add(MODAL, component);
		mainCard.showCard(MODAL);
		currentModal = component;
		for (Action stAction : startActions.values()) {
			stAction.setEnabled(false);
		}
	}

	public void resetModal() {
		if (currentModal == null) {
			return;
		}
		mainCard.remove(currentModal);
		currentModal = null;
		Action activeStart = startActions.get(activeApplication);
		for (Action stAction : startActions.values()) {
			stAction.setEnabled(activeStart != stAction);
		}
		menuCard.showCard(activeApplication.getTitle());
		mainCard.showCard(activeApplication.getTitle());
	}

	public JFrame mainFrame() {
		return mainFrame;
	}

	/**
	 * @return the main frame of the application
	 */
	private JFrame build() {
		if (mainFrame != null) {
			return mainFrame;
		}
		mainFrame = new JFrame("FIXME : Main Application Name (configurable)");
		mainFrame.addWindowListener(new ApplicationWindowListener());
		// mainFrame.setUndecorated(true);
		mainPanel = new JPanel(new BorderLayout());

		mainCard = new CardPanel();
		menuCard = new CardPanel();
		for (Application application : applications) {
			mainCard.add(application.getTitle(), application.getComponent());
			menuCard.add(application.getTitle(), buildMenu(application));
		}

		menuCard.add(MODAL, buildToolbarPanel());

		mainPanel.add(menuCard, BorderLayout.NORTH);
		mainPanel.add(mainCard, BorderLayout.CENTER);
		mainPanel.add(buildMainNavigation(), BorderLayout.SOUTH);
		if (applications.size() > 0) {
			activateApplication(applications.get(0));
		}
		mainFrame.getContentPane().add(mainPanel, BorderLayout.CENTER);
		mainFrame.getContentPane().add(status, BorderLayout.SOUTH);
		mainFrame.setPreferredSize(new Dimension(1024, 768));
		mainFrame.pack();
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		mainFrame.setIconImage(SwingUtil.loadIcon("jtrade.icon.16.png")
				.getImage());
		return mainFrame;

	}

	public void setStatus(String text) {
		status.setText(text);
	}

	private void activateApplication(Application application) {
		if (application == null) {
			throw new NullPointerException(
					"null application can not be activated");
		}
		if (!applications.contains(application)) {
			throw new IllegalArgumentException("Application "
					+ application.getTitle() + " not added!");
		}
		menuCard.showCard(application.getTitle());
		mainCard.showCard(application.getTitle());
		mainFrame.setTitle("JTrade : " + application.getTitle());
		if (activeApplication != null) {
			startActions.get(activeApplication).setEnabled(true);
		}
		application.activate();
		activeApplication = application;
		startActions.get(application).setEnabled(false);

	}

	private JComponent buildMenu(Application application) {
		JPanel applicationToolbar = buildToolbarPanel();
		for (Action action : application.getToolbarActions()) {
			applicationToolbar.add(createToolbarButton(action, 48));
		}
		return applicationToolbar;
	}

	private JPanel buildToolbarPanel() {
		JPanel modalToolbar = new GradientPanel(new FlowLayout(
				FlowLayout.CENTER),
				getToolBarBackground(),
				getToolBarMirror());
		return modalToolbar;
	}

	private JComponent buildMainNavigation() {
		JPanel applicationToolbar = new JPanel(new FlowLayout(FlowLayout.CENTER));
		applicationToolbar.setOpaque(false);
		for (Application application : applications) {
			Action starter = new ApplicationStartAction(application);
			startActions.put(application, starter);
			JButton button = createToolbarButton(starter, 48);
			applicationToolbar.add(button);
		}
		JPanel panel = new GradientPanel (new BorderLayout(),
				getToolBarBackground(),
				getToolBarMirror());
		panel.add(applicationToolbar, BorderLayout.CENTER);
		JPanel systemToolbar = new JPanel(new FlowLayout(FlowLayout.RIGHT));
		systemToolbar.setOpaque(false);
		systemToolbar.add(createToolbarButton(helpAction, 48));
		systemToolbar.add(createToolbarButton(exitAction, 48));
		panel.add(systemToolbar, BorderLayout.EAST);
		return panel;
	}

	private class ApplicationExitAction extends AbstractAction {

		private static final long serialVersionUID = 1L;

		private static final String TOOLTIP = "action.exit.tooltip";
		private static final String ACTION_NAME = "action.exit.name";
		private static final String ICON = "action.exit.icon";

		private Runnable exit;

		public ApplicationExitAction() {
			super(PROPERTIES.getString(ACTION_NAME), PROPERTIES.getIcon(ICON));
			putValue(Action.SHORT_DESCRIPTION, PROPERTIES.getString(TOOLTIP));
			exit = new Runnable() {
				@Override
				public void run() {
					for (Application application : applications) {
						if (!application.allowsExit()) {
							return;
						}
					}
					System.exit(0);
				}
			};
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			SwingUtil.finishCurrentEdit();
			SwingUtilities.invokeLater(exit);
		}
	}

	private class ApplicationWindowListener extends WindowAdapter {
		@Override
		public void windowClosing(WindowEvent e) {
			for (Application application : applications) {
				if (!application.allowsExit()) {
					return;
				}
			}
		}
	}

	private class ApplicationStartAction extends AbstractAction {

		private static final long serialVersionUID = 1L;
		private final Application application;

		private ApplicationStartAction(Application application) {
			super(application.getShortName(), application.getIcon());
			this.application = application;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			activateApplication(application);
		}

	}

	private static JButton createToolbarButton(Action action, int widthInDLU) {
		final JButton button = new JButton(action) {
			private static final long serialVersionUID = 1L;

			@Override
			public Point getToolTipLocation(MouseEvent event) {
				Point location = MouseInfo.getPointerInfo().getLocation();
				SwingUtilities.convertPointFromScreen(location, this);
				// The tool tip should be shown at the bottom-right side of
				// the mouse cursor. AWT does not have an API to determine
				// the size of the current mouse cursor, hence the hardcoded
				// values.
				location.x += 16;
				location.y += 20;
				return location;
			}
		};
		button.setFocusable(false);
		button.setOpaque(false);
		// button.setForeground(Color.BLACK);
		button.setBorder(BorderFactory.createEmptyBorder());
		button.setVerticalTextPosition(SwingConstants.BOTTOM);
		button.setHorizontalTextPosition(SwingConstants.CENTER);
		Dimension prefSize = button.getPreferredSize();
		prefSize.width = Sizes.dialogUnitXAsPixel(widthInDLU, button);
		button.setPreferredSize(prefSize);
		button.setSize(prefSize);
		button.setMinimumSize(prefSize);
		button.setMaximumSize(prefSize);
		return button;
	}

    public Color getToolBarBackground() {
    	return Color.DARK_GRAY;
    }
    public Color getToolBarMirror() {
    	return Color.LIGHT_GRAY;
    }
}
